﻿using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Media.Animation;
using HandyControl.Data;

namespace HandyControl.Interactivity;

[DefaultTrigger(typeof(UIElement), typeof(EventTrigger), "MouseLeftButtonDown")]
[DefaultTrigger(typeof(ButtonBase), typeof(EventTrigger), "Click")]
public abstract class TriggerAction : Animatable, IAttachedObject
{
    public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.Register("IsEnabled",
        typeof(bool), typeof(TriggerAction), new FrameworkPropertyMetadata(ValueBoxes.TrueBox));

    private readonly Type _associatedObjectTypeConstraint;

    private DependencyObject _associatedObject;
    private bool _isHosted;

    internal TriggerAction(Type associatedObjectTypeConstraint)
    {
        _associatedObjectTypeConstraint = associatedObjectTypeConstraint;
    }

    protected DependencyObject AssociatedObject
    {
        get
        {
            ReadPreamble();
            return _associatedObject;
        }
    }

    protected virtual Type AssociatedObjectTypeConstraint
    {
        get
        {
            ReadPreamble();
            return _associatedObjectTypeConstraint;
        }
    }

    public bool IsEnabled
    {
        get => (bool) GetValue(IsEnabledProperty);
        set => SetValue(IsEnabledProperty, ValueBoxes.BooleanBox(value));
    }

    internal bool IsHosted
    {
        get
        {
            ReadPreamble();
            return _isHosted;
        }
        set
        {
            WritePreamble();
            _isHosted = value;
            WritePostscript();
        }
    }

    public void Attach(DependencyObject dependencyObject)
    {
        if (!Equals(dependencyObject, AssociatedObject))
        {
            if (AssociatedObject != null)
                throw new InvalidOperationException(ExceptionStringTable
                    .CannotHostTriggerActionMultipleTimesExceptionMessage);
            if (dependencyObject != null &&
                !AssociatedObjectTypeConstraint.IsInstanceOfType(dependencyObject))
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
                    ExceptionStringTable.TypeConstraintViolatedExceptionMessage,
                    new object[]
                        {GetType().Name, dependencyObject.GetType().Name, AssociatedObjectTypeConstraint.Name}));
            WritePreamble();
            _associatedObject = dependencyObject;
            WritePostscript();
            OnAttached();
        }
    }

    public void Detach()
    {
        OnDetaching();
        WritePreamble();
        _associatedObject = null;
        WritePostscript();
    }

    DependencyObject IAttachedObject.AssociatedObject =>
        AssociatedObject;

    internal void CallInvoke(object parameter)
    {
        if (IsEnabled)
            Invoke(parameter);
    }

    protected override Freezable CreateInstanceCore()
    {
        return (Freezable) Activator.CreateInstance(GetType());
    }

    protected abstract void Invoke(object parameter);

    protected virtual void OnAttached()
    {
    }

    protected virtual void OnDetaching()
    {
    }
}
